package tcp

import (
	"errors"
	"net"
	"sync"
	"sync/atomic"
)

type ClientConfig struct {
	Host        string
	SendChanMax int
	RecvChanMax int
}

type Client struct {
	conn      *Conn
	config    *ClientConfig
	callback  Callback
	stopFlag  int32
	exitChan  chan interface{}
	waitGroup *sync.WaitGroup
}

func NewClient(callback Callback, config *ClientConfig) *Client {
	cl := &Client{
		config:    config,
		callback:  callback,
		exitChan:  make(chan interface{}),
		waitGroup: &sync.WaitGroup{},
	}

	return cl
}

func (cl *Client) Start() error {
	if cl.IsClosed() {
		return errors.New("use of closed client")
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp4", cl.config.Host)
	if err != nil {
		return err
	}

	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	}

	cl.conn = NewConn(conn, cl)
	go cl.conn.run()

	return nil
}

func (cl *Client) IsClosed() bool {
	return atomic.LoadInt32(&cl.stopFlag) == 1
}

func (cl *Client) Close() {
	if atomic.LoadInt32(&cl.stopFlag) == 0 {
		atomic.StoreInt32(&cl.stopFlag, 1)
		cl.conn.Close()
		close(cl.exitChan)
		cl.waitGroup.Wait()
	}
	//fmt.Println("exit client")
}

func (cl *Client) getCallback() Callback {
	return cl.callback
}

func (cl *Client) getExitChan() chan interface{} {
	return cl.exitChan
}

func (cl *Client) getSendChanMax() int {
	return cl.config.SendChanMax
}

func (cl *Client) getRecvChanMax() int {
	return cl.config.RecvChanMax
}

func (cl *Client) getWaitGroup() *sync.WaitGroup {
	return cl.waitGroup
}
